#include "carray.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

void carray_init (CArray* carray, int elem_size, int chunk_size)
{
	assert(carray != NULL);
	assert(elem_size > 0);
	assert(chunk_size > 0);
	
	carray->elem_size = elem_size;
	carray->chunk_size = chunk_size;
	carray->count = 0;
	cbuf_init (&carray->blocks, sizeof(void*), 1);
	
	void* block = malloc (carray->elem_size * chunk_size);
	cbuf_push (&carray->blocks, &block);
}

void carray_cleanup (CArray* carray)
{
	assert(carray != NULL);
	
	for (void* ptr = carray->blocks.begin; ptr != carray->blocks.end; ptr++) {
		free (*((void**) ptr));
	}
	
	cbuf_cleanup (&carray->blocks);
	
	carray->elem_size = 0;
	carray->chunk_size = 0;
	carray->count = 0;
}

void* carray_append (CArray* carray, void* elem)
{
	assert(carray != NULL);
	
	if (carray->count >= carray->blocks.count * carray->chunk_size) {
		void* block = malloc (carray->elem_size * carray->chunk_size);
		cbuf_push (&carray->blocks, &block);
	}
	
	void* last_block = *CBUF_HEAD(&carray->blocks,void*);
	int index = carray->count++ % carray->chunk_size;
	void* elem_ptr = last_block + index * carray->elem_size;
	
	// fill the new element with data only if provided
	if (elem != NULL) {
		memcpy (elem_ptr, elem, carray->elem_size);
	}
	
	return elem_ptr;
}

void* carray_elem (CArray* carray, int index)
{
	assert(carray != NULL);
	assert((index >= 0) && (index < carray->count));
	
	int block_index = index / carray->chunk_size;
	int elem_index = index % carray->chunk_size;
	void* block = *CBUF_ELEM(&carray->blocks,block_index,void*);
	return block + elem_index * carray->elem_size;
}

#ifdef TEST_CARRAY
#include <stdio.h>

int main ()
{
	CArray array;
	carray_init (&array, sizeof(int), 10);
	
	for (int i = 0; i < 5; i++) {
		carray_append (&array, &i);
	}
	
	int* ptr[5];
	
	for (int i = 0; i < 5; i++) {
		ptr[i] = CARRAY_ELEM(&array,i,int);
		printf("Ptr. %d = %d\n", i, *ptr[i]);
	}
	
	for (int i = 5; i < 15; i++) {
		carray_append (&array, &i);
	}
	
	for (int i = 0; i < 5; i++) {
		printf("Ptr. %d = %d\n", i, *ptr[i]);
	}
	
	for (int i = 0; i < 15; i++) {
		int elem = *CARRAY_ELEM(&array,i,int);
		printf("Nr. %d = %d\n", i, elem);
	}
	
	carray_cleanup (&array);
	
	return 0;
}

#endif
